#!/usr/bin/perl
# 该程序用来计算步进电机加速度的查找表
#

use strict;
use warnings;

my $timerclk = 120.0e6;             # 定时器T0频率
my $sysclk;                        # PCA0频率
my $microstep = 2;                # 细分数
my $factor = 2 / 16;            # 计算值系数
my $facilitymicro = $microstep/$factor;            # 物理细分步数

my $speed = 2500;                # 最高速度, 0.01 rad/s
my $accel = 6000;                # 加速度, 0.01 rad/s^2
my $decel = 6000;                # 减速度, 0.01 rad/s^2
my $min_speed = 62.8;            # 最小转速, 0.01 rad/s
my $max_c = 32767;            # 最大cn值 max = 32768 25000

my $speed_interval_code = 0;    # 显示表间隔
my $speed_interval = 1 << $speed_interval_code;

my $fspr = 200;                    # 每圈整步步数

my $table_type = "unsigned short";        # 表类型


my $pi = 3.14159265;
my $spr = $fspr * $microstep;    # 细分后一圈的步数
my $alpha = 2 * $pi / $spr;        # 每步转角, rad

my $pca_freq = $speed / (2 * $pi * 100) * $spr;        # PCA0输出最大频率
my $timer_div = int($timerclk / ($pca_freq * 2));    # T0溢出值

$timer_div = 3;

#$timer_div = 32768 if ($timer_div > 32768);
#$timer_div = 1 if ($timer_div < 1);

#my $timer_reload = 256 - $timer_div;                # T0 重装值
$sysclk = $timerclk / $timer_div;                    # PCA基频

# 调试数据, 最大/小输出频率和转速
my $max_freq_output = $sysclk / 2;
my $min_freq_output = $sysclk / (2 * $max_c);
my $max_speed_output = $max_freq_output / $spr * 2 * $pi * 100;
my $min_speed_output = $min_freq_output / $spr * 2 * $pi * 100;

my $c0 = $sysclk  * sqrt(2 * $alpha * 100 / $accel);
my $min_delay = $alpha * $sysclk  / ($speed / 100);

#
#                    2C[n-1]
#  C[n] = C[n-1] - ----------                 (1)
#                    4n + 1
#
#
#        C[n-1] + C[n]
#  n = -------------------                    (2)
#       4 (C[n-1] - C[n])
#
#
#
#  C[n] = C[0] ( sqrt(n+1) - sqrt(n) )        (3)
#
#
#
#        C[0]      C[n]   2
#  n = (------- - -------)                    (4)
#        2C[n]     2C[0]
#
#

my @counter = ($c0);

# 先按(3)计算, 等C[n-1] - C[n] < 1时, 按(4)计算
for (my $n = 1; ; $n++) {
my $cn = $c0 * (sqrt($n + 1) - sqrt($n));
last if ($cn < $min_delay);
#last if (int($cn) == int($counter[$n-1]));
push @counter, $cn * $factor;
}

# 按(4)计算
#for (;;) {
#    my $cn = int($counter[$#counter] - 1);
#    last if $cn < $min_delay;

#    my $n = $c0 / (2 * $cn) - $cn / (2 * $c0);
#    $n = int($n * $n);
#    while($#counter < $n) {
#        push @counter, $cn;
#    }
#}

# 对counter的求和, $sum_table[n] = $counter[0] + ... + $counter[n]
my @sum_table = ();
my $sum = 0;

for (my $n = 0; $n < scalar @counter; $n++) {
    $counter[$n] /= 2;
    if ($counter[$n] >= $max_c) {
        $counter[$n] = $max_c;
        $sum += $max_c;
    }elsif ($counter[$n] < 1) {
        $counter[$n] = 1;
        $sum += 1;
    }
    else{
        $sum += int($counter[$n]);
    }
    push @sum_table, $sum;
}

# 求和表数据类型和打印宽度
my ($sum_type, $sum_width) = $sum > 65535 ? ("long", 8) : ("ushort", 4);

my $min_speed_n = int($min_speed * $min_speed / 100 / (2 * $alpha * $accel));


# 输出C文件代码

my $h_file_name = "../DevicePro/freq_table.h";
my $c_file_name = "../DevicePro/freq_table.c";

open(my $h_file, ">", $h_file_name) or die "Can't open $h_file_name:$!";
open(my $c_file, ">", $c_file_name) or die "Can't open $c_file_name:$!";

my $count = int(scalar @counter / $speed_interval);

print $h_file <<EOF;
#ifndef _FREQ_TABLE_H_
#define _FREQ_TABLE_H_

//
// 由freq.pl自动生成, 请不要修改
//
// 最大输出频率/转速: $max_freq_output / $max_speed_output
// 最小输出频率/转速: $min_freq_output / $min_speed_output
//
#define MICROSTEP                $microstep                // 软件使用细分步数
#define FACILITYMICRO            $facilitymicro            // 设备细分步数
#define FSPR                     $fspr                // 每转整步数
#define SPR                      $spr                // 转一圈的步数
#define ALPHA                    $alpha    // 每步角度, rad
#define SPEED_INTERVAL_CODE      $speed_interval_code                // 表间隔, 2^n
#define SPEED_TABLE_SIZE         $count
#define ACCEL                    $accel            // 加速度, 0.01 rad/s^2
#define DECEL                    $decel            // 减速度, 0.01 rad/s^2
#define SPEED                    $speed            // 最高速度, 0.01 rad/s
#define MIN_SPEED_INDEX          $min_speed_n          // 最小速度输出索引值
#define MAX_C                    $max_c                // 最大计数器值
#define FREQ_TABLE(n)            freq_table[(n) >> SPEED_INTERVAL_CODE]
#define SUM_TABLE(n)             sum_table[(n) >> SPEED_INTERVAL_CODE]
#define TIME_TABLE(n)            (freq_table[(n) >> SPEED_INTERVAL_CODE] ? \\
                                 freq_table[(n) >> SPEED_INTERVAL_CODE] : 25000)


extern const $table_type freq_table[SPEED_TABLE_SIZE];
extern $sum_type const sum_table[SPEED_TABLE_SIZE];

#endif // _FREQ_TABLE_H_
EOF

print $c_file <<EOF;
#include "user_global.h"
#include "$h_file_name"

const $table_type freq_table[SPEED_TABLE_SIZE] =
{
EOF

print $c_file "\t";
for(my $s = 0; $s < $count; $s++) {
printf $c_file "0x%02X, ", @counter[$s * $speed_interval];
if ($s % 8 == 7) {
print $c_file "\n\t";
}
}
print $c_file "\n};\n";



print $c_file "\n/*\n";
print $c_file "[index]   [ID ]        Hz       rps       rpm     speed\n";
my $index = 0;
#for(my $s = $max_c; $s > 0;) {
#        my $freq = $sysclk / 2 / $s * $factor;
#    my $rps = $freq / $spr;
#    my $rpm = $rps * 60;
#    my $speed = $rps * 2 * $pi * 100;
#        printf $c_file "[%3d]%10X%10.1f%10.2f%10.1f%10.1f\n", $index, $s, $freq, $rps, $rpm, $speed;
#        $s -= 50;
#        $index += 50;
#}

for(my $s = 0; $s < $count; $s++)  {
my $freq = $sysclk / 2 / @counter[$s * $speed_interval] * $factor;
my $rps = $freq / $spr;
my $rpm = $rps * 60;
my $speed = $rps * 2 * $pi * 100;
printf $c_file "[%3d]%10X%10.1f%10.2f%10.1f%10.1f\n", $s, @counter[$s * $speed_interval], $freq, $rps, $rpm, $speed;
# $s -= 50;
#$index += 50;
}
print $c_file "*/\n";


close($c_file);
